﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;


namespace TankIt
{
    public class Engine
    {

        List<Wall> walls;
        List<Bullet> balas=new List<Bullet>();
        Explosion explosao;
        Vector2 playerOdlPos;
        float playeRotationOld;

        double timestamp;
        
        Tank enemy;
        Tank player;

        Texture2D texBala;
        Texture2D texExplo;

        SoundEffect fire;
        SoundEffect somMovimento;

        SoundEffectInstance somMovimentoInst;

        public bool left = false;
        public bool right = false;
        public bool frente = false;
        public bool back = false;
        public bool disparar = false;

        public bool IsJoined = false;

        ContentManager content;

        private const string GROUP_ADDRESS = "224.0.1.11";
        private const int GROUP_PORT = 54329;


        UdpAnySourceMulticastChannel Channel;

        //Converter comunicador;

        Unmarshled un=new Unmarshled("");

        public Engine(ContentManager content)
        {
            this.content = content;
        }


        public void createTank()
        {
            player = new Tank(1.0f,content,new Vector2(100,100),(float)Math.PI,this);
            enemy = new Tank(1.0f, content, new Vector2(700, 380), 0.0f, this);

            player.load();
            enemy.load();
        }


        public void turnRight(GameTimerEventArgs e)
        {
            player.rotate(0.05f,e);
           
        }

        public void turnLeft(GameTimerEventArgs e)
        {
            player.rotate(-0.05f,e);
            
        }


        public void draw(SpriteBatch sb)
        {
            player.draw(sb);
            enemy.draw(sb);

            foreach (Wall w in walls)
                w.draw(sb);

            foreach (Bullet b in balas)
                b.draw(sb);

                explosao.Draw(sb);
        }

        public void update(GameTimerEventArgs e)
        {
            if (left == true)
            {
                turnLeft(e);
            }

            if(right==true){
                turnRight(e);
            }

            if (player.heat>0) player.heat--;
            
            
            player.Update(e,frente,back);

            if (right || left || frente || back)
            {
                if (somMovimentoInst.State == SoundState.Stopped)
            {
                somMovimentoInst.Volume = 0.6f;
                somMovimentoInst.IsLooped = true;
                somMovimentoInst.Play();
            }
            else
                somMovimentoInst.Resume();
            }

            else {
                if (somMovimentoInst.State == SoundState.Playing)
                    somMovimentoInst.Pause();
            }

            foreach (Bullet b in balas)
                b.update(e);

            if (playerOdlPos != player.position || playeRotationOld != player.rotation || disparar)
            {
                playerOdlPos = player.position;
                playeRotationOld = player.rotation;
                //comunicador.enviaDados(player.position.X, player.position.Y, player.rotation, disparar);
                this.Channel.Send(GameCommands.JoinFormat, "");
            }

            //Unmarshled un = comunicador.receive(player.position.X,player.position.Y);

            if (un.position.X != -1)
                enemy.Update(e, un);

            if (un.fire) dispara('e');
            if (disparar)
            {
                dispara('p');
                disparar = false;
            }

            checkBullets();

            explosao.Update(e);



        }

        public void load()
        {
            createTank();
            walls = new List<Wall>();
            walls.Add(new Wall(1.0f,content,new Vector2(400,4),0,'v',this));
            walls.Add(new Wall(1.0f, content, new Vector2(400, 476),0, 'v', this));
            walls.Add(new Wall(1.0f, content, new Vector2(4, 240), 0, 'h', this));
            walls.Add(new Wall(1.0f, content, new Vector2(796, 240), 0, 'h', this));
            texBala = content.Load<Texture2D>("Missile");
            texExplo = content.Load<Texture2D>("explosions");
            

            fire = content.Load<SoundEffect>("Fire");
            somMovimento = content.Load<SoundEffect>("Tankmoving");
            somMovimentoInst = somMovimento.CreateInstance();

            explosao = new Explosion(texExplo, 0, 256, 64, 64, 16,content);


            insereParedes();
            foreach (Wall w in walls)
                w.load();

            this.Channel = new UdpAnySourceMulticastChannel(GROUP_ADDRESS, GROUP_PORT);
            Join("");

        }

        public void checkBullets()
        {
            int i=balas.Count;
            int j = walls.Count;
            int aux=0;
            Bullet act;
            while (i>0)
            {
                act = balas[i-1];

                if (player.bb.Intersects(act.bb))
                {
                    player.takeDamage(50);
                    balas.RemoveAt(i-1);
                    explosao.setPosition(act.getPositionExplosion());
                    
                }
                else
                {
                    if (enemy.bb.Intersects(act.bb)) {
                        balas.RemoveAt(i-1);
                        explosao.setPosition(act.getPositionExplosion());
                       }
                    else{
                        aux=0;
                        while(aux<j){
                                if (walls[aux].bb.Intersects(act.bb)){
                                    balas.RemoveAt(i-1);
                                    explosao.setPosition(act.getPositionExplosion());
                                    if (walls[aux].type == 'b') walls.RemoveAt(aux);
                                    aux=j;
                                }
                            aux++;
                            }
                        
                    }
                }

                i--;
            }
        }

        public Boolean testColisions(BoundingSphere b)
        {
           if (enemy.bb.Intersects(b))
                 return true;

            foreach (Wall w in walls)
            {
                if (w.bb.Intersects(b))
                    return true;
            }

            return false;
        }

        public void dispara(char quem)
        {
            Vector2 position;
            float rotation;
            if (quem == 'p')
            {
                if (player.heat > 0) return;
                else player.heat += 50;
                position = player.getPosition();
                rotation = player.getRotation();
            }
            else {
                position = enemy.getPosition();
                rotation = enemy.getRotation();
            }

            float oldx = 150;
            float oldy = 0;
            float posx = 50.1f;
            float posy = 0;
            Vector2 speed =Vector2.Zero;

            speed.X = oldx * (float)Math.Cos(rotation) - oldy * (float)Math.Sin(rotation);
            speed.Y = oldy * (float)Math.Cos(rotation) + oldx * (float)Math.Sin(rotation);
            position.X -= posx * (float)Math.Cos(rotation) - posy * (float)Math.Sin(rotation);
            position.Y -= posy * (float)Math.Cos(rotation) + posx * (float)Math.Sin(rotation);

            speed *= -1;
            
            balas.Add(new Bullet(position, texBala, rotation, speed));
            fire.Play();

        }

        #region Paredes
        public void insereParedes()
        {
            
            //desenhar o P
            walls.Add(new Wall(1.0f,content,new Vector2 (30,200),0.0f,'b',this));
            walls.Add(new Wall(1.0f, content, new Vector2(30, 240), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(30, 280), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(30, 320), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(30, 360), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(30, 400), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(70, 200), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(110, 200), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(110, 240), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(110, 280), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(70, 280), 0.0f, 'b', this));

            //desenhar o I
            walls.Add(new Wall(1.0f, content, new Vector2(220, 30), 0.0f, 's', this));
            walls.Add(new Wall(1.0f, content, new Vector2(220, 120), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(220, 160), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(220, 200), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(220, 240), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(220, 280), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(220, 320), 0.0f, 'b', this));

            //desenhar o 1º Z
            walls.Add(new Wall(1.0f, content, new Vector2(280, 210), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(320, 210), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(360, 210), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(400, 210), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(380, 250), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(360, 290), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(340, 330), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(320, 370), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(300, 410), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(280, 450), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(320, 450), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(360, 450), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(400, 450), 0.0f, 'b', this));


            //desenhar o 2º Z
            walls.Add(new Wall(1.0f, content, new Vector2(400, 130), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(440, 130), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(480, 130), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(520, 130), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(500, 170), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(480, 210), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(460, 250), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(440, 290), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(420, 330), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(400, 370), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(440, 370), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(480, 370), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(520, 370), 0.0f, 'b', this));


            //desenhar o A
            walls.Add(new Wall(1.0f, content, new Vector2(640, 60), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(620, 100), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(600, 140), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(580, 180), 0.0f, 's', this));
            walls.Add(new Wall(1.0f, content, new Vector2(560, 220), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(540, 260), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(660, 100), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(680, 140), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(700, 180), 0.0f, 's', this));
            walls.Add(new Wall(1.0f, content, new Vector2(720, 220), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(740, 260), 0.0f, 'b', this));
            walls.Add(new Wall(1.0f, content, new Vector2(620, 180), 0.0f, 's', this));
            walls.Add(new Wall(1.0f, content, new Vector2(660, 180), 0.0f, 's', this));


        }
        #endregion

        #region Multicast Communication
        /// <summary>
        /// Register for events on the multicast channel.
        /// </summary>
        private void RegisterEvents()
        {
            // Register for events from the multicast channel
            this.Channel.Joined += new EventHandler(Channel_Joined);
            this.Channel.BeforeClose += new EventHandler(Channel_BeforeClose);
            this.Channel.PacketReceived += new EventHandler<UdpPacketReceivedEventArgs>(Channel_PacketReceived);
        }

        /// <summary>
        /// Unregister for events on the multicast channel
        /// </summary>
        private void UnregisterEvents()
        {
            if (this.Channel != null)
            {
                // Register for events from the multicast channel
                this.Channel.Joined -= new EventHandler(Channel_Joined);
                this.Channel.BeforeClose -= new EventHandler(Channel_BeforeClose);
                this.Channel.PacketReceived -= new EventHandler<UdpPacketReceivedEventArgs>(Channel_PacketReceived);
            }
        }
        /// <summary>
        /// Handles the BeforeClose event of the Channel control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        void Channel_BeforeClose(object sender, EventArgs e)
        {
            //this.Channel.Send(String.Format(GameCommands.Leave, _playerName));
        }

        /// <summary>
        /// Handles the Joined event of the Channel.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        void Channel_Joined(object sender, EventArgs e)
        {
            this.IsJoined = true;
            this.Channel.Send(GameCommands.JoinFormat, "");
        }

        /// <summary>
        /// Handles the PacketReceived event of the Channel control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="SilverlightPlayground.UDPMulticast.UdpPacketReceivedEventArgs"/> instance containing the event data.</param>
        void Channel_PacketReceived(object sender, UdpPacketReceivedEventArgs e)
        {
            string message = e.Message.Trim('\0');
            string[] messageParts = message.Split(GameCommands.CommandDelimeter.ToCharArray());

            if (messageParts.Length == 1)
            {
                switch (messageParts[0])
                {
                    case GameCommands.Join:
                        //OnPlayerJoined(new PlayerInfo(messageParts[1], e.Source));
                        break;
                }


            }
            else if (messageParts.Length == 5)
            {
                un = new Unmarshled(message);
            }
            else
            {
                // Ignore messages that don't have the expected number of parts.
            }
        }
        #endregion

        /// <summary>
        /// Join the multicast group.
        /// </summary>
        /// <param name="playerName">The player name I want to join as.</param>
        /// <remarks>The player name is not needed for multicast communication. it is 
        /// used in this example to identify each member of the multicast group with 
        /// a friendly name. </remarks>
        public void Join(string playerName)
        {
            if (IsJoined)
            {
                return;
            }

            // Store my player name
            //_playerName = playerName;

            //Open the connection
            this.Channel.Open();
        }

        /// <summary>
        /// Leave the current game
        /// </summary>
        public void LeaveGame()
        {
            //if (this.Channel != null && _currentOpponent != null)
            //{
            //    // Tell the opponent
            //    this.Channel.SendTo(_currentOpponent.PlayerEndPoint, GameCommands.LeaveGameFormat, _playerName);
            //    _currentOpponent = null;
            //}
        }

    }
}
